package com.zidongxiangxi.rabbit.producer.manager;

import com.zidongxiangxi.rabbit.core.constant.enums.MessageSendStatusEnum;
import com.zidongxiangxi.rabbit.producer.constant.ProducerConstants;
import com.zidongxiangxi.rabbit.producer.entity.Producer;
import com.zidongxiangxi.rabbit.producer.mapper.ProducerMapper;
import com.zidongxiangxi.rabbit.producer.sql.ProducerSqlProvider;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayInputStream;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 发送消息的管理类
 *
 * @author chenxudong
 * @date 2020/11/07
 */
public class ProducerManager {
	private final ProducerSqlProvider sqlProvider;
	private final ProducerMapper producerMapper;
	private final JdbcTemplate jdbcTemplate;

	public ProducerManager(String tableName, JdbcTemplate jdbcTemplate) {
		this.sqlProvider = new ProducerSqlProvider(tableName);
		this.producerMapper = new ProducerMapper();
		this.jdbcTemplate = jdbcTemplate;
	}

	@Transactional(rollbackFor = Exception.class)
	public Long saveProducer(Producer producer) {
		if (Objects.isNull(producer.getMaxRetryTimes()) || producer.getMaxRetryTimes() > ProducerConstants.MAX_RETRY_TIMES) {
			producer.setMaxRetryTimes(ProducerConstants.MAX_RETRY_TIMES);
		}
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date());
		calendar.add(Calendar.SECOND, 20);

		KeyHolder keyHolder = new GeneratedKeyHolder();
		PreparedStatementCreator preparedStatementCreator = connection -> {
			PreparedStatement preparedStatement = connection.prepareStatement(sqlProvider.getInsertMqSql(),
					Statement.RETURN_GENERATED_KEYS);
			preparedStatement.setString(0, producer.getApplication());
			preparedStatement.setString(1, producer.getHost());
			preparedStatement.setInt(2, producer.getPort());
			preparedStatement.setString(3, producer.getVirtualHost());
			preparedStatement.setInt(4, producer.getType());
			preparedStatement.setString(5, producer.getExchange());
			preparedStatement.setString(6, producer.getRoutingKey());
			preparedStatement.setString(7, producer.getMessageId());
			preparedStatement.setBlob(8, new ByteArrayInputStream(producer.getMessage()));
			preparedStatement.setInt(9, MessageSendStatusEnum.SENDING.getValue());
			preparedStatement.setInt(10, producer.getMaxRetryTimes());
			preparedStatement.setDate(11, new java.sql.Date(calendar.getTime().getTime()));
			return preparedStatement;
		};

		int rows = jdbcTemplate.update(preparedStatementCreator, keyHolder);
		if (rows < 1 || Objects.isNull(keyHolder.getKey())) {
			return null;
		}
		return keyHolder.getKey().longValue();
	}

	@Transactional(rollbackFor = Exception.class)
	public boolean failSendMq(String application, String messageId) {
		Producer producer = null;
		try {
			producer = jdbcTemplate.queryForObject(sqlProvider.getSelectMqSql(), producerMapper, application, messageId);
		} catch (EmptyResultDataAccessException ignore) {}
		if (Objects.isNull(producer)) {
			return false;
		}
		int retryTimes = producer.getRetryTimes() + 1;
		retryTimes = Math.max(retryTimes, 1);
		int rows;
		if (retryTimes >= producer.getMaxRetryTimes()) {
			rows = jdbcTemplate.update(sqlProvider.getFailMqSql(), application, messageId);
		} else {
			int delaySecondIndex = Math.min(retryTimes, ProducerConstants.DELAY_SECONDS.length);
			delaySecondIndex = delaySecondIndex - 1;
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(new Date());
			calendar.add(Calendar.SECOND, ProducerConstants.DELAY_SECONDS[delaySecondIndex]);
			rows = jdbcTemplate.update(sqlProvider.getSendingMqSql(), retryTimes, calendar.getTime(), application, messageId);
		}
		return rows > 0;
	}

	@Transactional(rollbackFor = Exception.class)
	public boolean deleteMq(String application, String messageId) {
		int rows = jdbcTemplate.update(sqlProvider.getDeleteMqSql(), application, messageId);
		return rows > 0;
	}

	public List<Producer> listSendingMq(String application, int start, int limit) {
		return jdbcTemplate.query(sqlProvider.getListSendingMqSql(), producerMapper, application, start,
				limit);
	}

}
